/*
  AdViEmulator - AdventureVision emulator
  Copyright (C) 2012-2013  JustBurn

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef ADVIEMULATOR_H
#define ADVIEMULATOR_H

#include <queue>
#include <stdint.h>
#include "cpu8048.h"
#include "apucop411.h"

class AdViEmulator
{
	friend class CPU8048;
	friend class APUCOP411;

public:
	AdViEmulator(float samplerate = 44100.0f, int buffsamples = 65536);
	~AdViEmulator();

	// Emulation
	void Reset(void);
	int Emulate(bool sound, int mincycles, int minsndsamples = 0);
	void CPUSingleStep();
	void APUSingleStep();

	// States control
	// It really doesn't do anything here...
	enum {
		STATE_STOP,
		STATE_HALT,
		STATE_RUN
	};
	void SetState(bool run);
	int IsRunning();
	void SuspendState();
	void ResumeState();

	// Memory IO interface
	enum {
		NON_VALID,
		CPU_BIOS,
		CPU_IRAM,
		CPU_ROM,
		CPU_XRAM,
		APU_IROM,
		APU_IRAM
	};
	uint16_t MemSize(int type);
	uint8_t MemRead(int type, uint16_t addr);
	void MemWrite(int type, uint16_t addr, uint8_t data);
	uint8_t ReadROMData(uint16_t addr);
	int ReadROMType(uint16_t addr);
	bool IsBIOS(uint16_t addr);

	// Memory contents, ports and hw
	uint8_t BIOS[1024];
	uint8_t ROM[4096];
	uint8_t IRAM[64];
	uint8_t XRAM[1024];
	uint64_t cycCnt0;
	uint8_t GetP1(void);
	uint8_t GetP2(void);

	// Video
	void SetVideoMode(bool column, bool analog, int analogdecay);
	uint8_t displayPre[5];
	uint8_t displayPost[5];
	int columnsStrobe;
	uint8_t videoOut[384*5];
	uint16_t frameCnt;
	int analogDecay;
	uint8_t videoOutAD[384*40];

	// Breakpoint / Watchpoint
	enum {
		BREAK_POINT = 0,
		BREAK_EXCEPTION = 1,
		TRAP_READ = 4,
		TRAP_WRITE = 8,
		TRAP_ALL = (4 | 8)
	};
	bool breakExecution;
	int breakReason;
	char breakException[64];
	uint8_t BIOS_Trap[1024];
	uint8_t ROM_Trap[4096];
	uint8_t IRAM_Trap[64];
	uint8_t XRAM_Trap[1024];
	bool enableBreaks;
	void ClearAllBreaks(void);
	bool SetBreak(int type, int addr, int traps);
	int GetBreak(int type, int addr);

	// Trace PC
	void GetCPUTrace(int offset, uint8_t *type, uint16_t *addr);
	void GetAPUTrace(int offset, uint16_t *addr);

	// Sound IO
	int NumSamples();
	uint8_t GetSample();
	int GetSamples(uint8_t *buff, int samples);
	void SetVolume(uint8_t vol);
	uint8_t GetVolume();
	void SetAPUFreq(int freq);

	// Input
	enum {
		BUTTON_1 = 0,
		BUTTON_2 = 1,
		BUTTON_4 = 2,
		BUTTON_3 = 3,
		STICK_DOWN = 4,
		STICK_UP = 5,
		STICK_RIGHT = 6,
		STICK_LEFT = 7
	};
	void SetInput(uint8_t avInput, bool state);

	// Cores
	CPU8048 cpu;
	APUCOP411 apu;

protected:
	int state;
	uint8_t p1, p2, p2m;
	uint8_t avInput;
	void HardwarePoke(int faddr);
	void AddCPUTrace(void);
	void AddAPUTrace(void);
	int soundBuffSize;
	std::queue<uint8_t> soundFIFO;
	int soundPreset;
	int soundCounter;
	void AddSample(uint8_t sample);
	int8_t soundVolume1, soundVolume2;
	int cntAPUPreset;
	int cntAPUValue;
	bool APURunning;
	bool APURunningLast;
	bool videoAnalogDisplay;
	bool videoColumnMode;
	int cntVidPremod;
	int cntVidValue;
	int cntAnalogPremod;
	int cntAnalogValue;
	void AnalogDecay(void);
	void HardwareProc(int cyc);
	uint8_t traceCPUPtr;
	uint8_t traceCPUType[4096];
	uint16_t traceCPUAddr[4096];
	uint8_t traceAPUPtr;
	uint16_t traceAPUAddr[4096];
	uint8_t keyInput;
	void VideoLatch();
};

#endif // ADVIEMULATOR_H
